import logging
from decimal import Decimal
from threading import Thread

import phonenumbers
from django.contrib.auth.password_validation import validate_password
from django.db.models import ExpressionWrapper, F, IntegerField, DurationField, OuterRef, Count, Value, Subquery, When, \
    Case, FloatField, Q
from django.db.models.functions import TruncMonth, Now, ExtractDay, Round, Cast, Coalesce
from django.template.loader import get_template
from django_tenants.utils import schema_context
from rest_framework.exceptions import ValidationError
from rest_framework_simplejwt.views import TokenObtainPairView

from apps.users.filters import ProviderFilter, provider_filter_queryset
from apps.users.models import Provider, Domain, ProviderLicense, User, SystemAccounts, EtheraCredit, PromoCredit
from datetime import datetime
from django.db import transaction
import uuid
from django.shortcuts import get_object_or_404
from phonenumber_field.validators import validate_international_phonenumber
from rest_framework.response import Response
from rest_framework import status
from django.core.mail import EmailMultiAlternatives
from rest_framework.decorators import api_view, authentication_classes, permission_classes
from apps.core.pagination import paginated_queryset_response
from django.forms.models import model_to_dict
from .helpers import get_schema_name
from rest_framework.permissions import IsAdminUser
from .permissions import IsProvider
from .serializers import MyTokenObtainPairSerializer
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.tokens import AccessToken
from django.conf import settings
from rest_framework_simplejwt.tokens import RefreshToken

from .types import EtheraCreditStatusType, PromoCreditStatusType
from ..booking.models import Appointment
from ..booking.types import BookingStatusChoices, AppointmentStatusChoices
from ..client.models import Client
from ..billing.models import Invoice
from ..booking.types import BookingStatusChoices
from ..core.utils.gcs_operations import delete_file
from ..document.signals import save_soap_note, save_dap_note, save_treatment_plan, save_progress_note, save_gad_note, \
    save_intake_questionnaire, save_patient_health_questionnaire
from ..location.models import AdminBookingDetails, Room
from ..notification.models import ProviderNotificationSetting, ProviderReminderSetting
from apps.core.utils import date_time as datetime_helper, gcs_operations
from ..tenant.models import Calendar
from apps.tenant.models import InvoiceSetting, SuperBillSetting


class MyTokenObtainPairView(TokenObtainPairView):
    serializer_class = MyTokenObtainPairSerializer


def adding_default_settings_provider(provider):
    ProviderNotificationSetting.objects.create(fcm_id='')
    ProviderReminderSetting.objects.create()
    Calendar.objects.create(start_time="09:00", end_time="17:00")
    InvoiceSetting.objects.create(email=provider.email, subject="Invoice Setting for {{date}}",
                                  message="<p>Hi {{client_name}},</p><p></p><p>Attach please find {{"
                                          "number_of_invoices}}"
                                          "invoice(s) {{issue_date}}</p><p></p><p>Thank you.</p><p></p><p>{{"
                                          "provider_name}}</p><p>{{provider_email}}</p><p>{{"
                                          "provider_phone_number}}</p>")
    SuperBillSetting.objects.create(email=provider.email, subject="Invoice Setting for {{date}}",
                                    message="<p>Hi {{client_name}},</p><p></p><p>Attach please find {{"
                                            "number_of_invoices}}"
                                            "invoice(s) {{issue_date}}</p><p></p><p>Thank you.</p><p></p><p>{{"
                                            "provider_name}}</p><p>{{provider_email}}</p><p>{{"
                                            "provider_phone_number}}</p>")


def get_tokens_for_user(data):
    user = User.objects.get(email=data.get('email'))
    refresh = RefreshToken.for_user(user)
    return {
        'refresh': str(refresh),
        'access': str(refresh.access_token),
    }


def get_send_email_token(email):
    try:
        user = None
        while True:
            try:
                user = User.objects.get(email=email)
                break
            except User.DoesNotExist:
                continue
        if user:
            refresh = RefreshToken.for_user(user)
            response_token = {'refresh': refresh,
                              'access': str(refresh.access_token),
                              'user': user}
            return response_token
        else:
            raise Exception
    except Exception as ex:
        raise (ValidationError({'email': [ex]}))


def send_email_confirm_account(user, type_new_acc, provider=None):
    from_email = settings.CONTACT_EMAIL
    to = user.email
    token = get_send_email_token(to)['access']
    html_content = None
    subject = None
    if type_new_acc == 'PROVIDER':
        subject = 'Welcome to the Ethera Family!'
        html_template = get_template('users/welcome_new_provider.html')
        context = {
            'full_name_receiver': user.first_name + ' ' + user.last_name,
            'create_password_link': settings.PROVIDER_URL +
                                    '/create-new-password?user_type=provider&token={}'.format(token)
        }
        html_content = html_template.render(context)

    elif type_new_acc == 'CLIENT':
        subject = 'Please Set Up Your Ethera Client Portal'
        html_template = get_template('users/welcome_new_client.html')
        context = {
            'full_name': provider.first_name + ' ' + provider.last_name,
            'full_name_client': user.first_name + ' ' + user.last_name,
            'phone_number': provider.phone_number,
            'email': provider.email,
            'create_password_link': settings.CLIENT_URL +
                                    '/create-new-password?user_type=client&token={}'.format(token)

        }
        html_content = html_template.render(context)

    msg = EmailMultiAlternatives(subject=subject, from_email=from_email, to=[to])
    msg.attach_alternative(html_content, "text/html")

    try:
        msg.send()
    except Exception as ex:
        print(ex)

def send_email_reset_password(email):
    logger = logging.getLogger(__name__)
    subject = 'Reset password'
    from_email = settings.CONTACT_EMAIL
    to = email
    response = get_send_email_token(email)
    token = response['access']
    user = response['user']
    if user.is_provider is True:
        redirect_link = settings.PROVIDER_URL + \
                        '/reset-password?token={}'.format(token)
        verify_link = '<a href="{}">this link</a>'.format(redirect_link)
        text_content = 'Follow the link below to reset your password:'
        html_content = '<p>Click this link to reset password: {}<p>'.format(
            verify_link)
    if user.is_client is True:
        redirect_link = settings.CLIENT_URL + \
                        '/reset-password?token={}'.format(token)
        verify_link = '<a href="{}">this link</a>'.format(redirect_link)
        text_content = 'Follow the link below to reset your password:'
        html_content = '<p>Click this link to reset password: {}<p>'.format(
            verify_link)
    if user.is_staff is True:
        redirect_link = settings.ADMIN_URL + \
                        '/reset-password?token={}'.format(token)
        verify_link = '<a href="{}">this link</a>'.format(redirect_link)
        text_content = 'Follow the link below to reset your password:'
        html_content = '<p>Click this link to reset password: {}<p>'.format(
            verify_link)
    msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
    msg.attach_alternative(html_content, "text/html")
    try:
        msg.send()
    except Exception as ex:
        logger.error('Failed to send email with error {}'.format(str(ex)))


@api_view(('GET', 'POST', 'DELETE'))
@authentication_classes([JWTAuthentication])
@permission_classes([IsAdminUser | IsProvider])
@transaction.atomic
def providers_api(request):
    if request.method == 'GET':
        ordering = request.GET.get('ordering', '-created_at')
        queryset = Provider.objects.all()
        queryset = ProviderFilter.filter_queryset(self=None, request=request, queryset=queryset)

        queryset = queryset.order_by(ordering)
        providers = list()
        for query in queryset:
            provider_data = model_to_dict(query,
                                          exclude=['password', 'user_name', 'state', 'city', 'zipcode', 'address',
                                                   'credit_card', 'stripe_account_id', 'avatar'])

            provider_data['provider_license'] = list(query.provider_license.values('license_type', 'license_number',
                                                                                   'expire_date', 'state'))
            providers.append(provider_data)
        return paginated_queryset_response(queryset=providers, request=request)

    if request.method == 'POST':
        try:
            user = request.user
            data = request.data
            # data['date_of_birth'] = datetime.fromtimestamp(data['date_of_birth'])
            data['id'] = str(uuid.uuid4())
            provider_licenses = data.pop('provider_license', None)
            value = data['email']
            phone_number = data.get('phone_number')

            try:
                parsed_phone_number = phonenumbers.parse(phone_number, "US")

                # Check if the parsed phone number is valid
                if phonenumbers.is_valid_number(parsed_phone_number):
                    print("Phone number is valid:",
                          phonenumbers.format_number(parsed_phone_number, phonenumbers.PhoneNumberFormat.E164))
                else:
                    raise ValidationError("Phone number is invalid")
            except phonenumbers.phonenumberutil.NumberParseException:
                raise ValidationError("Invalid phone number format")
            email = value.lower()
            if User.objects.filter(email=email).count() > 0:
                raise ValidationError("Email already exist")
            data['schema_name'] = get_schema_name(data)
            if 'password' not in data:
                data['password'] = User.objects.make_random_password()
            provider = Provider.objects.create_user(**data)
            provider.is_provider = True
            provider.save()
            if provider_licenses:
                licenses = list()
                for provider_license in provider_licenses:
                    provider_license['expire_date'] = datetime.fromtimestamp(provider_license['expire_date'])
                    provider_license['provider'] = provider
                    licenses.append(ProviderLicense(**provider_license))
                ProviderLicense.objects.bulk_create(licenses)
            domain_name = str(provider.schema_name)
            domain = Domain.objects.create(domain=domain_name, is_primary=True, tenant=provider)
            domain.save()

            with schema_context(provider.schema_name):

                adding_default_settings_provider(provider)
                save_soap_note(provider)
                save_dap_note(provider)
                save_treatment_plan(provider)
                save_progress_note(provider)
                save_gad_note(provider)
                save_intake_questionnaire(provider)
                save_patient_health_questionnaire(provider)

            if user.is_staff:
                thread = Thread(target=send_email_confirm_account, args=[provider, 'PROVIDER'])
                thread.start()

            provider_data = model_to_dict(provider,
                                          exclude=['password', 'user_name', 'state', 'city', 'zipcode', 'address', 'avatar',
                                                   'credit_card', 'stripe_account_id', 'groups', 'user_permissions'])
            provider_data['id'] = provider.id

            provider_data['provider_license'] = list(provider.provider_license.values('license_type', 'license_number',
                                                                                      'expire_date', 'state'))

            return Response(provider_data, status=status.HTTP_201_CREATED)
        except ValidationError as e:
            raise ValidationError({"msg": e.detail})


@api_view(('GET', 'DELETE', 'PATCH'))
@authentication_classes([JWTAuthentication])
@permission_classes([IsAdminUser | IsProvider])
@transaction.atomic
def get_provider_api(request, provider_id):
    provider = get_object_or_404(Provider, id=provider_id)
    if request.method == 'GET':
        provider_data = model_to_dict(provider,
                                      exclude=['password', 'user_name', 'state', 'city', 'zipcode', 'address',
                                               'credit_card', 'stripe_account_id', 'groups', 'user_permissions'])
        provider_data['avatar'] = provider.avatar.url if provider.avatar else None

        # provider_data['provider_license'] = list(provider.provider_license.values('license_type', 'license_number',
        #                                                                           'expire_date', 'state'))

        provider_data['provider_license'] = []
        for license_data in provider.provider_license.all():
            expire_timestamp = int(license_data.expire_date.timestamp()) if license_data.expire_date else None
            provider_data['provider_license'].append({
                'license_type': license_data.license_type,
                'license_number': license_data.license_number,
                'expire_date': expire_timestamp,  # Add Unix timestamp to provider_data
                'state': license_data.state
            })

        return Response(provider_data, status=status.HTTP_200_OK)

    if request.method == 'DELETE':
        if request.user.is_provider is True:
            return Response({"details": "You do not have permission to perform this action"},
                            status=status.HTTP_400_BAD_REQUEST)
        provider.delete()

        return Response({"msg": "Provider Deleted"}, status=status.HTTP_204_NO_CONTENT)

    if request.method == 'PATCH':
        data = request.data
        licenses = None
        if 'provider_license' in data:
            licenses = data.pop('provider_license', None)

        if 'date_of_birth' in data:
            data.pop('date_of_birth')

        if 'phone_number' in data:
            phone_number = data.get('phone_number')

            try:
                parsed_phone_number = phonenumbers.parse(phone_number, "US")

                # Check if the parsed phone number is valid
                if phonenumbers.is_valid_number(parsed_phone_number):
                    print("Phone number is valid:",
                          phonenumbers.format_number(parsed_phone_number, phonenumbers.PhoneNumberFormat.E164))
                else:
                    raise ValidationError("Phone number is invalid")
            except phonenumbers.phonenumberutil.NumberParseException:
                raise ValidationError("Invalid phone number format")

        # Update all fields with the provided data
        for field_name, field_value in data.items():
            if hasattr(Provider, field_name):
                setattr(provider, field_name, field_value)

        provider.save()

        if licenses is not None:
            ProviderLicense.objects.filter(provider=provider).delete()
            for license in licenses:
                if 'formated_expire_date' in license:
                    license.pop('formated_expire_date')
                license['expire_date'] = datetime.fromtimestamp(license['expire_date'])
                ProviderLicense.objects.create(provider=provider, **license)

        provider_data = model_to_dict(provider,
                                      exclude=['password', 'user_name', 'state', 'city', 'zipcode', 'address', 'avatar',
                                               'credit_card', 'stripe_account_id', 'groups', 'user_permissions'])

        provider_license_data = []
        for license in provider.provider_license.all():
            license_data = {
                'license_type': license.license_type,
                'license_number': license.license_number,
                'expire_date': int(license.expire_date.timestamp()) if license.expire_date else None,
                'state': license.state,
            }
            provider_license_data.append(license_data)
        provider_data['provider_license'] = provider_license_data
        provider_data['id'] = provider_data.pop('user_ptr')

        return Response(provider_data, status=status.HTTP_200_OK)


@api_view(['POST'])
@transaction.atomic
@authentication_classes([JWTAuthentication])
@permission_classes([IsAdminUser])
def provider_bulk_delete(request):
    try:
        Provider.objects.filter(id__in=request.data['ids']).delete()
        # print(providers)
        # for provider in providers:
        #     provider.delete()
        return Response({"msg": "Providers are Deleted"}, status=status.HTTP_204_NO_CONTENT)
    except Exception as e:
        return Response({"msg": e.detail}, status=status.HTTP_400_BAD_REQUEST)


@api_view(['GET'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def provider_me(request):
    try:
        if request.method == 'GET':
            provider_id = request.user.id
            provider = Provider.objects.filter(id=provider_id).first()
            if provider:
                provider_data = model_to_dict(provider,
                                              exclude=['password', 'user_name', 'state', 'city', 'zipcode',
                                                       'address', 'credit_card', 'stripe_account_id', 'groups',
                                                       'user_permissions'])
                get_assigned_url = provider_data['avatar'].url if provider_data['avatar'] else ''
                provider_data['avatar'] = get_assigned_url
                provider_license_data = []
                for license in provider.provider_license.all():
                    license_data = {
                        'license_type': license.license_type,
                        'license_number': license.license_number,
                        'other_license': license.other_license if license.other_license else '',
                        'expire_date': int(license.expire_date.timestamp()) if license.expire_date else None,
                        'state': license.state,
                    }
                    provider_license_data.append(license_data)
                provider_data['provider_license'] = provider_license_data
                provider_data['id'] = provider_data.pop('user_ptr')
                return Response(provider_data, status=status.HTTP_200_OK)
            else:
                return Response({"msg": "Not found"}, status=status.HTTP_204_NO_CONTENT)
    except Exception as e:
        return Response({
            'success': False, 'message': f'Failed to get provider information because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)


@api_view(['POST'])
@authentication_classes([])
@permission_classes([])
def create_password_confirm(request):
    if request.method == 'POST':
        data_request = request.data
        if data_request['id']:
            new_password = data_request['password']
            validate_password(new_password)

            # confirm_policies = data_request['confirm_policies']
            # if not confirm_policies:
            #     raise ValidationError({
            #         'Confirm Policies': [
            #             'Please agree to the Terms and Conditions!'
            #         ]
            #     })

            user = User.objects.get(id=data_request['id'])
            user.set_password(new_password)
            user.save()
            return Response(status=status.HTTP_200_OK)
        return Response(status=status.HTTP_400_BAD_REQUEST)


DAYS_OF_A_MONTH = 30.0


@api_view(['GET'])
@authentication_classes([JWTAuthentication])
@permission_classes({IsAdminUser})
def provider_bookings_api(request):
    if request.method == 'GET':

        offset = int(request.query_params.get("offset", 0))
        limit = int(request.query_params.get("limit", 10))
        queryset = Provider.objects.all().order_by('-created_at')
        queryset = provider_filter_queryset(request, queryset)
        queryset = queryset.annotate(
            date_joined_trunc=TruncMonth('date_joined'),
            join_diff=ExpressionWrapper(
                TruncMonth(Now()) - F('date_joined_trunc'),
                DurationField()),
            join_diff_months=(
                Cast(Round(ExtractDay('join_diff') / DAYS_OF_A_MONTH),
                     IntegerField())),
        )

        bookings = AdminBookingDetails.objects.annotate(
            duration=(F('end_time') - F('start_time')))
        bookings = bookings.filter(provider=OuterRef('pk'), ).values('provider')

        # total_bookings
        total_bookings = bookings.annotate(count=Count('id')).values('count')

        # sum_booking_to_current_month
        now = datetime_helper.now()

        bookings_to_current_months = bookings.filter(
            created_at__gt=now.start_of('month'))

        sum_booking_to_current_month = bookings.filter(
            created_at__lt=now.start_of('month'))

        sum_booking_to_current_month = sum_booking_to_current_month.annotate(
            count=Count('id')).values('count')

        # sum_bookings_to_last_30_days
        sum_booking_to_last_thirty_days = bookings.filter(
            created_at__gt=now.subtract(months=1)
        ).annotate(count=Count('id')).values('count')

        # count_cancelled_to_current_month
        cancelled_to_current_months = bookings_to_current_months.filter(
            status=BookingStatusChoices.CANCELED.value)

        count_cancelled_to_current_month = cancelled_to_current_months. \
            annotate(count=Count('id')).values('count')

        queryset = queryset.annotate(
            total_bookings=Coalesce(
                Subquery(total_bookings, IntegerField()),
                Value(0)),
            total_bookings_to_thirty_days=Coalesce(
                Subquery(sum_booking_to_last_thirty_days, IntegerField()),
                Value(0)),
            total_booking_to_current_month=Coalesce(
                Subquery(sum_booking_to_current_month, IntegerField()),
                Value(0)),
            count_cancelled_to_current_month=Coalesce(Subquery(
                count_cancelled_to_current_month, IntegerField()),
                Value(0)),
            avg_booking_per_month=Case(
                When(join_diff_months__gt=0, then=(
                        F('total_booking_to_current_month') /
                        F('join_diff_months'))),
                default=Value(0),
                output_field=IntegerField()),
            avg_cancelled_per_month=Case(
                When(join_diff_months__gt=0, then=(
                        Coalesce(F('count_cancelled_to_current_month'), 0) /
                        F('join_diff_months'))),
                default=Value(0),
                output_field=FloatField()),
        )

        data = []
        print("hello")
        for instance in queryset:
            formatted_data = {"id": instance.id, "first_name": instance.first_name, "last_name": instance.last_name,
                              "middle_name": instance.middle_name, "suffix": instance.suffix,
                              "date_of_birth": instance.date_of_birth, "practice": instance.practice,
                              "email": instance.email, "phone_number": instance.phone_number, "npi": instance.npi,
                              "npi_two": instance.npi_two, "tax": instance.tax, "tax_two": instance.tax_two,
                              "caqh": instance.caqh, "taxonomy": instance.taxonomy, "avatar": instance.avatar.url if instance.avatar else None,
                              "status": instance.status,
                              "total_bookings": instance.total_bookings if instance.total_bookings is not None else 0,
                              "avg_booking_per_month": instance.avg_booking_per_month if instance.avg_booking_per_month is not None else 0,
                              "total_bookings_to_thirty_days": instance.total_bookings_to_thirty_days if instance.total_bookings_to_thirty_days is not None else 0,
                              "avg_cancelled_per_month": instance.avg_cancelled_per_month if instance.avg_cancelled_per_month is not None else 0,
                              "created_at": instance.created_at, "provider_license": instance.provider_license.values()}

            # for license in instance.provider_license.all():
            #     license_data = {
            #         # Access fields of the ProviderLicense model here
            #     }
            #     formatted_data["provider_license"].append(license_data)

            data.append(formatted_data)

        return Response({
            "result": data[offset:offset + limit],
            "offset": offset,
            "limit": limit,
            "count": len(data)
        })


@api_view(['GET', 'POST'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAdminUser])
def user_list(request):
    """
    List staff users or create a new staff user.
    """
    if request.method == 'GET':
        # Filter staff users
        users = SystemAccounts.objects.filter(is_staff=True)

        data = []
        for user in users:
            data.append({
                'id': user.id,
                'avatar': user.avatar if user.avatar else None,
                'first_name': user.first_name,
                'last_name': user.last_name,
                'is_kiosk': user.is_kiosk,
                'is_welcome_board': user.is_welcome_board,
                'is_staffs': user.is_staffs,
                'email': user.email,
                'is_staff': user.is_staff,
                'is_superuser': user.is_superuser,
            })
        return paginated_queryset_response(data, request)
    elif request.method == 'POST':
        # Create a new staff user
        email = request.data.get('email')
        if not email:
            return Response({'error': 'Email is required'}, status=status.HTTP_400_BAD_REQUEST)
        password = request.data.get('password')
        try:
            # Check for existing email
            if User.objects.filter(email=email.lower()).exists():
                raise ValidationError("Email already exists")
            first_name = request.data.get('first_name', None)
            last_name = request.data.get('last_name', None)

            # Create user with hashed password
            user = SystemAccounts.objects.create_user(
                email=email.lower(),
                is_staff=True,
                password=User.objects.make_random_password(),
                is_staffs=True
            )

            if first_name:
                user.first_name = first_name
            if last_name:
                user.last_name = last_name
            validate_password(password)
            user.set_password(password)
            user.save()
            return Response({
                'id': user.id,
                'email': user.email,
                'token': 'placeholder_token',  # Replace with actual token generation logic
                'is_staff': user.is_staff,
            })
        except ValidationError as e:
            return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)


@api_view(['GET', 'PATCH', 'DELETE'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAdminUser])
def user_detail(request, user_id=None):
    """
    Retrieve, update, or delete a staff user.
    """
    try:
        user = SystemAccounts.objects.get(pk=user_id, is_staff=True)
    except SystemAccounts.DoesNotExist:
        return Response({'error': 'User not found'}, status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':
        data = {
            'id': user.id,
            'avatar': user.avatar if user.avatar else None,
            'first_name': user.first_name,
            'last_name': user.last_name,
            'is_kiosk': user.is_kiosk,
            'is_welcome_board': user.is_welcome_board,
            'email': user.email,
            'is_staff': user.is_staff,
            'is_staffs': user.is_staffs,
            'is_superuser': user.is_superuser,
        }
        return Response(data)
    elif request.method == 'PATCH':
        # Update user details (including password)
        email = request.data.get('email')
        if email and email.lower() != user.email:
            # Check for existing email if updated
            if User.objects.filter(email=email.lower()).exists():
                return Response({'error': 'Email already exists'}, status=status.HTTP_400_BAD_REQUEST)
            user.email = email.lower()

        user.first_name = request.data.get('first_name', user.first_name)
        user.last_name = request.data.get('last_name', user.last_name)

        # Handle password update (optional)
        password = request.data.get('password')
        if password:
            if len(password) < 6:
                return Response({'error': 'Password must be at least 6 characters long'},
                                status=status.HTTP_400_BAD_REQUEST)
            user.set_password(password)  # Use set_password for password hashing

        user.save()
        return Response({
            'id': user.id,
            'email': user.email,
            'is_staff': user.is_staff,
        })
    elif request.method == 'DELETE':
        if user.is_superuser:
            return Response({'error': 'You cannot delete the superuser account'}, status=status.HTTP_403_FORBIDDEN)
        user.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

    return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)


@api_view(['GET', 'POST'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAdminUser])
def manage_service_accounts(request):
    """
    List, create, or delete service accounts (welcome boards or kiosks).
    """
    if request.method == 'GET':
        # Filter users based on service account flags
        users = SystemAccounts.objects.filter(Q(is_welcome_board=True) | Q(is_kiosk=True)).all()
        data = []
        for user in users:
            data.append({
                'id': user.id,
                'avatar': user.avatar if user.avatar else None,
                'first_name': user.first_name,
                'last_name': user.last_name,
                'is_kiosk': user.is_kiosk,
                'is_welcome_board': user.is_welcome_board,
                'email': user.email,
                'is_staff': user.is_staff,
                'is_superuser': user.is_superuser,
            })
        return paginated_queryset_response(data, request)  # Set safe=False for non-primitive data

    elif request.method == 'POST':
        # Create a new service account
        try:
            account_type = request.data.get('type')
            email = request.data.get('email')
            password = request.data.get('password')

            if not account_type or account_type not in ("welcome_board", "kiosk"):
                return ValidationError({'error': 'Invalid account type'}, status=400)

            if not email:
                return ValidationError({'error': 'Email is required'}, status=400)

            if not password or len(password) < 6:
                return ValidationError({'error': 'Password must be at least 6 characters long'}, status=400)

            # Check for existing email
            if User.objects.filter(email=email.lower()).exists():
                raise ValidationError("Email already exists")

            # Create user with hashed password
            user = SystemAccounts.objects.create_user(
                email=email.lower(),
                password=User.objects.make_random_password(),
                is_superuser=False,
                is_staff=False,
                is_provider=False,
                is_client=False,
            )
            user.set_password(password)
            user.save()
            if account_type == "welcome_board":
                user.is_welcome_board = True
            else:
                user.is_kiosk = True
            user.save()
            return Response({'message': 'Account created successfully'}, status=201)

        except ValidationError as e:
            return Response({'msg': str(e)}, status=status.HTTP_400_BAD_REQUEST)


@api_view(['GET', 'DELETE'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAdminUser])
def manage_service_accounts_id(request, user_id):
    if request.method == 'DELETE':
        user = get_object_or_404(SystemAccounts, id=user_id)
        user.delete()
        return Response({'msg': 'User has been deleted'}, status=status.HTTP_204_NO_CONTENT)


@api_view(['GET'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAdminUser])
def provider_room_api(request):
    if request.method == 'GET':
        ordering = request.GET.get('ordering', '-created_at')
        queryset = Provider.objects.all().order_by(ordering).values('id', 'first_name', 'last_name')
        return (paginated_queryset_response(queryset, request))


@api_view(['GET'])
@permission_classes([IsProvider])
def retrieve_invoice_client(request, pk=None):
    current_datetime = datetime_helper.now()
    queryset = Client.objects.all()
    # # Subquery to get the previous appointment for each client
    # previous_appointment_subquery = Appointment.objects.filter(
    #     clients=OuterRef('pk'),
    #     start_date__date__lte=current_datetime.date()
    # ).exclude(status=AppointmentStatusChoices.CANCELLED.value).order_by(
    #     '-start_date')
    # previous_appointment_subquery = previous_appointment_subquery.exclude(
    #     start_date__date=current_datetime.date(),
    #     start_time__gt=current_datetime.time()
    # ).values('start_date')[:1]
    #
    # # Subquery to get the next appointment for each client
    # next_appointment_subquery = Appointment.objects.filter(
    #     clients=OuterRef('pk'),
    #     start_date__date__gte=current_datetime.date(),
    # ).exclude(status=AppointmentStatusChoices.CANCELLED.value)
    # next_appointment_subquery = next_appointment_subquery.exclude(start_date__date=current_datetime.date(),
    #                                                               end_time__lt=current_datetime.time()).order_by(
    #     'start_date').values('start_date')[:1]
    #
    # queryset = queryset.annotate(
    #     latest_appointment=Subquery(previous_appointment_subquery),
    #     next_appointment=Subquery(next_appointment_subquery)
    # )

    if pk is not None:
        client = queryset.get(pk=pk)
        appointments = Appointment.objects.filter(
            Q(clients=client.id, start_date__date__gte=current_datetime.date()) |
            Q(booking__start_date__date__gte=current_datetime.date(), clients=client.id)
        ).exclude(status=AppointmentStatusChoices.CANCELLED.value).order_by('-start_date').values('start_date',
                                                                                                 'booking__start_date').first()

        previous_appointments = Appointment.objects.filter(
            Q(clients=client.id, start_date__date__lte=current_datetime.date()) |
            Q(booking__start_date__date__lte=current_datetime.date(), clients=client.id)
        ).exclude(status=AppointmentStatusChoices.CANCELLED.value).order_by('start_date').values('start_date',
                                                                                                  'booking__start_date').first()
        latest_appointment=None
        previous_appointment = None
        if appointments:
            if appointments['start_date']:
                latest_appointment = appointments.get('start_date').timestamp()
            elif appointments['booking__start_date']:
                latest_appointment = appointments.get('booking__start_date').timestamp()

        if previous_appointments:
            if previous_appointments['start_date']:
                previous_appointment = previous_appointments.get('start_date').timestamp()
            elif previous_appointments['booking__start_date']:
                previous_appointment = previous_appointments.get('booking__start_date').timestamp()


        client_data = {
            'id': client.id,
            'first_name': client.first_name,
            'middle_name': client.middle_name,
            'last_name': client.last_name,
            'email': client.email,
            'status': client.status,
            'date_started': client.date_started,
            'phone_number': client.phone_number,
            'messaging': client.messaging,
            'client_address': [],
            'next_appointment': latest_appointment if latest_appointment else None,
            'latest_appointment': previous_appointment if previous_appointment else None,
        }
        for address in client.client_address.all():
            client_data['client_address'].append({
                'id': address.id,
                'address': address.address,
                # Add more fields if needed
            })

        return Response(client_data)
    else:
        return Response(status=404)


@api_view(['POST', 'GET'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAdminUser])
def create_ethera_credit(request, provider_pk=None):
    if request.method == 'POST':
        if provider_pk is None:
            return Response({"msg": "Provider id can not be none"}, status=204)

        data = request.data.copy()
        data['provider'] = provider_pk

        provider = Provider.objects.filter(pk=provider_pk).first()
        if not provider:
            return Response({"msg": "Provider does not exist"}, status=204)
        status = data.get('status')
        amount = Decimal(data.get('amount', 0))


        if int(status) == EtheraCreditStatusType.REMOVE.value:
            if provider.ethera_credit <= 0:
                raise ValidationError('Ethera Credit is already zero')
            if (provider.ethera_credit - amount) < 0:
                raise ValidationError('You cannot remove more credits than available')
            provider.ethera_credit -= amount
        else:
            provider.ethera_credit += amount

        provider.save()

        EtheraCredit.objects.create(
            provider=provider,
            status=status,
            amount=amount,
            notes=data.get('notes')
        )

        return Response({"msg": "Ethera Credit has been updated"}, status=201)
    if request.method == 'GET':
        provider = Provider.objects.filter(id=provider_pk).first()
        if provider:
            ethera_credits = EtheraCredit.objects.filter(provider=provider)
            credits = []
            for credit in ethera_credits:
                ethera_credit = model_to_dict(credit)
                ethera_credit['date'] = credit.date.timestamp()
                credits.append(ethera_credit)

            return paginated_queryset_response(credits, request)
        else:
            return Response({'msg': 'Provider not found'}, status=204)

@api_view(['POST'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAdminUser])
def bulk_create_ethera_credit(request):
    ids = request.data.get('ids', [])
    amount = Decimal(request.data.get('amount', 0))
    status = request.data.get('status', EtheraCreditStatusType.ADD.value)
    notes = request.data.get('notes')

    list_data_create = Provider.objects.filter(pk__in=ids)
    if len(list_data_create) == 0:
        raise ValidationError('Invalid Ids for provider')

    credits = []
    if int(status) == EtheraCreditStatusType.REMOVE.value:
        for provider in list_data_create:
            if provider.ethera_credit <= 0:
                raise ValidationError('Ethera Credit is already zero')
            provider.ethera_credit -= amount
            provider.save()
            credit = EtheraCredit(provider=provider, status=status, amount=amount, notes=notes)
            credits.append(credit)
    else:
        for provider in list_data_create:
            provider.ethera_credit += amount
            provider.save()
            credit = EtheraCredit(provider=provider, status=status, amount=amount, notes=notes)
            credits.append(credit)

    EtheraCredit.objects.bulk_create(credits)

    return Response({"msg": "Selected Providers Ethera Credit Updated"}, status=201)


@api_view(['POST', 'GET'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAdminUser])
def create_promo_credit(request, provider_pk=None):
    if request.method == 'POST':

        if not provider_pk:
            return Response("Provider id cannot be none", status=400)

        amount = Decimal(request.data.get('amount', 0))
        status = request.data.get('status', PromoCreditStatusType.ADD.value)
        notes = request.data.get('notes', None)
        provider = Provider.objects.filter(pk=provider_pk).first()
        if not provider:
            return Response({"msg": "Provider does not exist"}, status=204)

        if int(status) == PromoCreditStatusType.REMOVE.value:
            if provider.promo_credit <= 0:
                return Response("Promo Credit is already zero", status=400)
            if (provider.promo_credit - amount) < 0:
                return Response("You cannot remove more credits than available", status=400)
            provider.promo_credit -= amount
        else:
            provider.promo_credit += amount

        provider.save()

        promo_credit = PromoCredit.objects.create(
            provider=provider,
            amount=amount,
            status=status,
            notes=notes
        )

        return Response({"msg": "Promo Credit has been updated"}, status=201)

    if request.method == 'GET':
        provider = Provider.objects.filter(id=provider_pk).first()
        if provider:
            promo_credits = PromoCredit.objects.filter(provider=provider)
            credits = []
            for credit in promo_credits:
                promo_credit = model_to_dict(credit)
                promo_credit['date'] = credit.date.timestamp()
                credits.append(promo_credit)

            return paginated_queryset_response(credits, request)
        else:
            return Response({'msg': 'Provider not found'}, status=204)


@api_view(['POST'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAdminUser])
def bulk_create_promo_credit(request):
    ids = request.data.get('ids', [])
    amount = Decimal(request.data.get('amount', 0))
    status = request.data.get('status', PromoCreditStatusType.ADD.value)
    notes = request.data.get('notes', None)
    list_data_create = Provider.objects.filter(pk__in=ids)
    if len(list_data_create) == 0:
        raise ValidationError('Invalid Ids for provider')

    credits = []
    with transaction.atomic():
        for provider in list_data_create:
            if int(status) == PromoCreditStatusType.REMOVE.value:
                if provider.promo_credit <= 0:
                    raise ValidationError('Promo Credit is already zero')
                provider.promo_credit -= amount
            else:
                provider.promo_credit += amount
            provider.save()

            credit = PromoCredit(
                provider=provider,
                amount=amount,
                status=status,
                notes=notes
            )
            credits.append(credit)

        PromoCredit.objects.bulk_create(credits)

    return Response({"msg": "Selected Providers Promo Credit Updated"}, status=201)


@api_view(['POST'])
@permission_classes([])
def reset_password_link(request):
    user = User.objects.filter(email=request.data['email']).first()
    if user:
        send_email_reset_password(request.data['email'])
        return Response(status=status.HTTP_200_OK)
    else:
        return Response({'msg': 'Email Does Not Exist'}, status=status.HTTP_400_BAD_REQUEST)


@api_view(['POST'])
@permission_classes([])
def reset_password_confirm_link(request):
    data_request = request.data
    if data_request['token']:
        try:
            decode_payload = AccessToken(data_request['token'])
            payload = decode_payload.payload
        except Exception as ex:
            raise ValidationError({'token': [ex]})
        new_password = data_request['password']
        validate_password(new_password)
        user = User.objects.get(id=payload['user_id'])
        user.set_password(new_password)
        user.save()
        return Response(status=status.HTTP_200_OK)
    return Response(status=status.HTTP_400_BAD_REQUEST)


@api_view(('POST', ))
@authentication_classes([JWTAuthentication])
@permission_classes([IsAdminUser | IsProvider])
@transaction.atomic
def user_image_api(request, user_pk):
        try:
            if not request.data.get('image'):
                return Response({
                    'success': False,
                    'message': 'Image is required'
                }, status=status.HTTP_400_BAD_REQUEST)
            get_provider = Provider.objects.filter(id=user_pk).first()
            if not get_provider:
                return Response({
                    'success': False,
                    'message': f'Failed to upload image because location does not exist'
                }, status=status.HTTP_400_BAD_REQUEST)
            get_image = request.FILES.getlist('image')[0]
            if get_provider.avatar:
                delete_file(get_provider.avatar.name)
            get_provider.avatar = get_image
            get_provider.save()
            return Response(
                {'avatar': get_provider.avatar.url if get_provider.avatar else None},
                status=status.HTTP_200_OK
            )
        except Exception as e:
            return Response({
                'success': False,
                'message': f'Failed to upload image because of {str(e)}'
            }, status=status.HTTP_400_BAD_REQUEST)


@api_view(('PATCH', ))
@authentication_classes([JWTAuthentication])
@permission_classes([IsAdminUser | IsProvider])
@transaction.atomic
def user_image_change_api(request, user_pk):
        try:
            if not request.data.get('image'):
                return Response({
                    'success': False,
                    'message': 'Image is required'
                }, status=status.HTTP_400_BAD_REQUEST)
            get_provider = Provider.objects.filter(id=user_pk).first()
            if not get_provider:
                return Response({
                    'success': False,
                    'message': f'Failed to upload image because location does not exist'
                }, status=status.HTTP_400_BAD_REQUEST)
            get_image = request.FILES.getlist('image')[0]
            if get_provider.avatar:
                delete_file(get_provider.avatar.name)
            get_provider.avatar = get_image
            get_provider.save()
            return Response(
                {'avatar': get_provider.avatar.url if get_provider.avatar else None},
                status=status.HTTP_200_OK
            )
        except Exception as e:
            return Response({
                'success': False,
                'message': f'Failed to upload image because of {str(e)}'
            }, status=status.HTTP_400_BAD_REQUEST)

@api_view(('DELETE', ))
@authentication_classes([JWTAuthentication])
@permission_classes([IsAdminUser | IsProvider])
@transaction.atomic
def user_image_delete_api(request, user_pk):
    try:
        get_provider = Provider.objects.filter(id=user_pk).first()
        if not get_provider:
            return Response({
                'success': False,
                'message': f'Failed to upload image because location does not exist'
            }, status=status.HTTP_400_BAD_REQUEST)
        if get_provider.avatar:
            delete_file(get_provider.avatar.name)
        get_provider.avatar = ''
        get_provider.save()
        return Response(
            {'msg': 'Provider image has been deleted'},
            status=status.HTTP_200_OK
        )
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed to upload image because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)